package controller;

import aplication.Rotulador;
import controller.dialog.DialogMessage;
import controller.util.MountAreaChart;
import data.RepositorioDeQuestoes;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.text.Text;
import model.Avaliacao;
import model.Questao;
import model.Rotulo;
import model.img.Filtro;
import model.img.HistGray;
import model.img.Histograma;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.highgui.Highgui;

/*
 * Centro de tudo...
 * Faz de tudo em poko...
 * implementa ChangeListener que é o evento de checar os combobox...
 */
public class ClassificacaoController implements Initializable, ChangeListener<Toggle> {

    /*
     * Dependencia do JavaFX, ou seja, referencia para os componentes que se deseja trabalhar
     * em tempo de execução
    */
    @FXML
    private Text name;
    @FXML
    private Text textTotal;
    @FXML
    private Text textQuestao;
    @FXML
    private RadioButton rbOpcaoUm;
    @FXML
    private RadioButton rbOpcaoDois;
    @FXML
    private RadioButton rbOpcaoTres;
    @FXML
    private RadioButton rbOpcaoQuatro;
    @FXML
    private RadioButton rbOpcaoCinco;
    @FXML
    private ImageView classficImage;
    @FXML
    private AnchorPane aCHImage;
    @FXML
    private AnchorPane aCHOpcaoUM;
    @FXML
    private AnchorPane aCHOpcaoDois;
    @FXML
    private AnchorPane aCHOpcaoTres;
    @FXML
    private AnchorPane aCHOpcaoQuatro;
    @FXML
    private AnchorPane aCHOpcaoCinco;
    @FXML
    private Button btConfirmar;
    @FXML
    private ProgressBar progressBar;
    @FXML
    private ComboBox cBFilters;
    
    //Auxiliares...
    private int numeroQuestao = 0;
    private Questao questao;
    private Avaliacao avaliacao;
    private Rotulo resposta;
    private Map<String, Filtro> filtros;
    private Filtro filtro;

    /*
     * metodo de inicialização, responsavel por carregar algumas opçoes
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        ToggleGroup group = new ToggleGroup();  //Grupo de radio butao
        avaliacao = RepositorioDeQuestoes.getInstance().getAvaliacao();
        rbOpcaoUm.setToggleGroup(group);
        rbOpcaoDois.setToggleGroup(group);
        rbOpcaoTres.setToggleGroup(group);
        rbOpcaoQuatro.setToggleGroup(group);
        rbOpcaoCinco.setToggleGroup(group);
        group.selectedToggleProperty().addListener(this);
        avaliacao.setQuestoes(RepositorioDeQuestoes.getInstance().getQuestoes().getQuestaos());
        loadStage();
        textTotal.setText("/ " + avaliacao.getQuestoes().size());
        definicaoDosFiltros();
        name.setText(avaliacao.getUsuario().getNome()); //Nome do cara
    }

    /*
     * Evento para confirmar a resposta...
     * Aqui são feitas as validações das respostas...
     */
    @FXML
    public void onConfimar(ActionEvent event) {
        if (questao.getPrimeiraTentativa() == null) {
            questao.setPrimeiraTentativa(resposta);
            if (validarResposta()) {
                showMessage("Resposta Correta!");
                questao.setAcerto(true);
                proximaQuestao();
            } else {
                showMessage("Resposta Errada!");
                loadHistograma();
            }
        } else if (questao.getSegundaTentativa() == null) {
            questao.setSegundaTentativa(resposta);
            if (validarResposta()) {
                showMessage("Resposta Correta!");
                questao.setAcerto(true);
                proximaQuestao();
            } else {
                questao.setAcerto(false);
                showMessage("Resposta Errada!");
                proximaQuestao();
            }
        }
    }

    /*
     * Filtro da mediana aplicada a imagem principal..
     */
    @FXML
    public void onMediana(ActionEvent event) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat imgMediana = null;
        try {
            imgMediana = Highgui.imread("./src/data/img/" + questao.getImagemPath(), 0);
        } catch (Exception e) {
            Rotulador.getLOGGER().logp(Level.SEVERE, getClass().getSimpleName(), "onMediana", "Imagem não encontrada!");
        }

        int colunas = imgMediana.cols();
        int linhas = imgMediana.rows();
        /*
         * Mediana
         */
        List<Double> mediana;
        for (int l = 1; l < linhas - 1; l++) {
            for (int c = 1; c < colunas - 1; c++) {
                mediana = new ArrayList<>();
                mediana.add((imgMediana.get(l, c)[0]));
                mediana.add((imgMediana.get(l - 1, c)[0]));
                mediana.add((imgMediana.get(l - 1, c - 1)[0]));
                mediana.add((imgMediana.get(l, c - 1)[0]));
                mediana.add((imgMediana.get(l + 1, c - 1)[0]));
                mediana.add((imgMediana.get(l + 1, c)[0]));
                mediana.add((imgMediana.get(l + 1, c + 1)[0]));
                mediana.add((imgMediana.get(l, c + 1)[0]));
                mediana.add((imgMediana.get(l - 1, c + 1)[0]));
                Collections.sort(mediana);
                imgMediana.put(l, c, mediana.get(4));
            }
        }
        setarImagem(imgMediana, classficImage);
    }

    /*
     * Filtro sal e pimenta aplicado na imagem principal..
     */
    @FXML
    public void onSalPimenta(ActionEvent event) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        Mat imgSP = null;
        try {
            imgSP = Highgui.imread("./src/data/img/" + questao.getImagemPath());
        } catch (Exception e) {
            Rotulador.getLOGGER().logp(Level.SEVERE, getClass().getSimpleName(), "onSalPimenta", "Imagem não encontrada!");
        }

        int colunas = imgSP.cols();
        int linhas = imgSP.rows();
        
        double[] auxZero = {0.0, 0.0, 0.0};
        double[] auxBranco = {255.0, 255.0, 255.0};
        int rand = (((colunas * linhas) / 10) / 2);
        Random randoColuna = new Random();
        Random randoLinha = new Random();
        for (int l = 1; l < rand - 1; l++) {
            imgSP.put(randoLinha.nextInt(linhas),
                    randoColuna.nextInt(colunas), auxZero);
            imgSP.put(randoLinha.nextInt(linhas),
                    randoColuna.nextInt(colunas), auxBranco);
        }
        setarImagem(imgSP, classficImage);
    }

    /*
     * Metodo para setar a imagem original ao ImageView...
     */
    @FXML
    public void onOriginal(ActionEvent event) {
        classficImage.setImage(questao.getImg());
    }

    /*
     * metodo para sair da avaliaçao...
     * Não faz o salvamento da avaliaçao no xml
     */
    @FXML
    public void onSair(ActionEvent event) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("/view/Principal.fxml"));
        Rotulador.getInstace().setStage(root);
    }

    /*
     * Metodo para setar a imagem no ImageView, tento como base um mat como base
     */
    private void setarImagem(Mat imgM, ImageView imageV) {
        MatOfByte matOfByte = new MatOfByte();
        Highgui.imencode(".jpg", imgM, matOfByte);
        byte[] byteArray = matOfByte.toArray();
        Image img = null;
        try {
            InputStream in = new ByteArrayInputStream(byteArray);
            img = new Image(in);
            imageV.setImage(img);
        } catch (Exception e) {
            Rotulador.getLOGGER().logp(Level.SEVERE, getClass().getSimpleName(), "setarImagem", "A imagem não foi montada!");
        }
    }

    /*
     * Metodo para popular cada questao na tela (todos os elementos).
     */
    public void loadStage() {
        questao = avaliacao.getQuestoes().get(numeroQuestao);
        classficImage.setImage(questao.getImg());
        if (rbOpcaoUm.isSelected()) {
            rbOpcaoUm.setSelected(false);
        }
        rbOpcaoUm.setText(questao.getRotulos().get(0).getDescricao());
        rbOpcaoDois.setText(questao.getRotulos().get(1).getDescricao());
        if (rbOpcaoDois.isSelected()) {
            rbOpcaoDois.setSelected(false);
        }
        rbOpcaoTres.setText(questao.getRotulos().get(2).getDescricao());
        if (rbOpcaoTres.isSelected()) {
            rbOpcaoTres.setSelected(false);
        }
        rbOpcaoQuatro.setText(questao.getRotulos().get(3).getDescricao());
        if (rbOpcaoQuatro.isSelected()) {
            rbOpcaoQuatro.setSelected(false);
        }
        rbOpcaoCinco.setText(questao.getRotulos().get(4).getDescricao());
        if (rbOpcaoCinco.isSelected()) {
            rbOpcaoCinco.setSelected(false);
        }

        //Controle do progress bar...
        progressBar.setProgress(progressBar.getProgress() + (1.0 / avaliacao.getQuestoes().size()));
        textQuestao.setText("" + (numeroQuestao + 1));
    }

    /*
     * metodo para carregar os histogramas na tela..
     */
    private void loadHistograma() {
        cBFilters.setVisible(true);
        cBFilters.getSelectionModel().selectFirst();

        writeHistogramaInScreen();
    }

    /*
     * metodo para renderizar os graficos na tela...
     */
    private void writeHistogramaInScreen() {
        resetOption(); //Limpar os graficos anteriores
        addProgress(); //Adicionar as bolinhas girando
        
        /*
         * metodo runnable para criar os graficos e os adicionar a tela...
         * Aki tem thread
         */
        Task<Void> task = new Task<Void>() {
            
            @Override
            protected Void call() throws Exception {
                final MountAreaChart graficoPrincipal = new MountAreaChart(filtro.filtrar(questao.getImagemPath()), 350, 255);
                graficoPrincipal.setLegendVisible(false);

                final MountAreaChart graficoDois = new MountAreaChart(questao.getRotulos().get(1).getHistograma(filtro), 200, 150);
                final MountAreaChart graficoTres = new MountAreaChart(questao.getRotulos().get(2).getHistograma(filtro), 200, 150);
                final MountAreaChart graficoQuatro = new MountAreaChart(questao.getRotulos().get(3).getHistograma(filtro), 200, 150);
                final MountAreaChart graficoCinco = new MountAreaChart(questao.getRotulos().get(4).getHistograma(filtro), 200, 150);
                final MountAreaChart graficoUM = new MountAreaChart(questao.getRotulos().get(0).getHistograma(filtro), 200, 150);
               /*
                * Adiciona os graficos na tela por meio da thread principal
                */
                Platform.runLater(new Runnable() {

                    @Override
                    public void run() {
                        resetOption();
                        aCHImage.getChildren().add(graficoPrincipal);
                        aCHOpcaoUM.getChildren().add(graficoUM);
                        aCHOpcaoDois.getChildren().add(graficoDois);
                        aCHOpcaoTres.getChildren().add(graficoTres);
                        aCHOpcaoQuatro.getChildren().add(graficoQuatro);
                        aCHOpcaoCinco.getChildren().add(graficoCinco);
                    }
                });
                return null;
            }
        };
        Thread thread = new Thread(task);
        thread.setDaemon(true);
        thread.start();
    }

    /*
     * Apresenta o dialogo na tela... (Nova Stage)
     */
    private void showMessage(String message) {
        DialogMessage.getIntancia().show(message);
    }

    /*
     * Metodo chamado ao final do questionario para renderizar a tela dos resultados..
     */
    private void loadResultado() {
        try {
            Parent root = FXMLLoader.load(getClass().getResource("/view/Resultado.fxml"));
            Rotulador.getInstace().setStage(root);
        } catch (IOException ex) {
            Rotulador.getLOGGER().logp(Level.SEVERE, "log.HandlerChangeHtml", "loadResultado", ex.getMessage());
        }
    }

    /*
     * Metodo auxiliar para validar a reposta da questao que esta sedo avaliada
     */
    private boolean validarResposta() {
        return resposta.getDescricao().equals(questao.getResposta().getDescricao());
    }

    /*
     * metodo para carregar o radiobutton do rotulo selecionado
     */
    @Override
    public void changed(ObservableValue<? extends Toggle> ov, Toggle velho, Toggle novo) {
        if (novo != null) {
            RadioButton radio = (RadioButton) novo;
            if (novo.isSelected()) {
                btConfirmar.setDisable(false);
                for (Rotulo r : questao.getRotulos()) {
                    if (r.getDescricao().equals(radio.getText())) {
                        resposta = r;
                        break;
                    }
                }
            }
        }
    }

    /*
     * Chama a proxima questao para ser renderizada na tela
     */
    private void proximaQuestao() {
        if (numeroQuestao < avaliacao.getQuestoes().size() - 1) {
            numeroQuestao++;
            resetOption();
            btConfirmar.setDisable(true);
            cBFilters.setVisible(false);
            loadStage();
        } else {
            loadResultado();
        }
    }

    /*
     * metodo para limpar os graficos e as bolinha rodiando
     */
    private void resetOption() {
        for (int i = 0; i < aCHImage.getChildren().size(); i++) {
            aCHImage.getChildren().remove(i);
            aCHOpcaoUM.getChildren().remove(i);
            aCHOpcaoDois.getChildren().remove(i);
            aCHOpcaoTres.getChildren().remove(i);
            aCHOpcaoQuatro.getChildren().remove(i);
            aCHOpcaoCinco.getChildren().remove(i);
        }
    }

    /*
     * Metodo que popula o comboBox de filtros (histograma) 
     * RGB
     * GRAY
     */
    private void definicaoDosFiltros() {
        filtros = new HashMap<>();
        Histograma histograma = new Histograma();
        filtros.put(histograma.getName(), histograma);

        HistGray histogramaGray = new HistGray();
        filtros.put(histogramaGray.getName(), histogramaGray);

        cBFilters.valueProperty().addListener(new ChangeListener<String>() {
            @Override
            public void changed(ObservableValue ov, String velho, String novo) {
                if (!filtro.getName().equals(novo)) {
                    filtro = filtros.get(novo);
                    writeHistogramaInScreen();
                }
            }
        });
        for (String key : filtros.keySet()) {
            cBFilters.getItems().add(key);
        }
        filtro = histograma;
    }

    /*
     * apenas para adicionar as bolinha que agira
     */
    private void addProgress() {
        ProgressIndicator progressIndicator = new ProgressIndicator(-1F);
        progressIndicator.setLayoutX(170);
        progressIndicator.setLayoutY(100);
        aCHImage.getChildren().add(progressIndicator);
        int x = 85;
        int y = 40;
        ProgressIndicator progressUM = new ProgressIndicator(-1F);
        progressUM.setLayoutX(x);
        progressUM.setLayoutY(y);
        aCHOpcaoUM.getChildren().add(progressUM);
        
        ProgressIndicator progressDois = new ProgressIndicator(-1F);
        progressDois.setLayoutX(x);
        progressDois.setLayoutY(y);
        aCHOpcaoDois.getChildren().add(progressDois);
        
        ProgressIndicator progressTres = new ProgressIndicator(-1F);
        progressTres.setLayoutX(x);
        progressTres.setLayoutY(y);
        aCHOpcaoTres.getChildren().add(progressTres);
        
        ProgressIndicator progressQuatro = new ProgressIndicator(-1F);
        progressQuatro.setLayoutX(x);
        progressQuatro.setLayoutY(y);
        aCHOpcaoQuatro.getChildren().add(progressQuatro);
        
        ProgressIndicator progressCinco = new ProgressIndicator(-1F);
        progressCinco.setLayoutX(x);
        progressCinco.setLayoutY(y);
        aCHOpcaoCinco.getChildren().add(progressCinco);
    }
}
